package com.ysz.lock;


import com.google.common.collect.Maps;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.utils.PathUtils;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName InterProcessLock
 * @Description 上锁解锁
 * @Data 13:54
 * @Version 1.0
 * @Author ysz
 */
public class InterProcessLock {
    //锁的具体内部实现类
    private final LockInternals lockInternals;
    //锁的根路径
    private final String basePath;
    //当用户使用同一个锁对象重复上锁时候，使用LockData的原子自增类完成
    private final ConcurrentMap<Thread, LockData> threadData = Maps.newConcurrentMap();
    //锁的根路径下的具体锁路径的前缀
    private static final String LOCK_NAME = "lock-";

    //封装锁路径
    static class LockData {
        private final Thread ourThread;
        private final String lockPath;
        private final AtomicInteger atomicInteger = new AtomicInteger(1);

        public LockData(Thread ourThread, String lockPath) {
            this.ourThread = ourThread;
            this.lockPath = lockPath;
        }
    }

    public InterProcessLock(CuratorFramework client, String path) {
        this(client, path, new StandardLockInternalsDriver());
    }

    public InterProcessLock(CuratorFramework client, String path, LockInternalsDriver driver) {
        this(client, path, LOCK_NAME, 1, driver);
    }

    public InterProcessLock(CuratorFramework client, String path, String lockName, int maxLeases, LockInternalsDriver driver) {
        this.basePath = PathUtils.validatePath(path);
        this.lockInternals = new LockInternals(client, driver, path, lockName, maxLeases);
    }

    //上锁
    public void acquire() throws Exception {
        if (!internalLock()) {
            throw new IOException("Lost connection while trying to acquire lock: " + basePath);
        }
    }

    //解锁
    public boolean release() {
        Thread thread = Thread.currentThread();
        LockData lockData = threadData.get(thread);
        if (Objects.isNull(lockData)) {
            throw new IllegalMonitorStateException("You do not own the lock: " + basePath);
        }
        int i = lockData.atomicInteger.decrementAndGet();
        //如果说 锁一次 释放一次 i应该是0
        //大于0表示锁了多次，此处不需要deletePath直接return
        if (i > 0) {
            return true;
        }
        if (i < 0) {
            //不要重复解锁
            //throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + basePath);
            return true;
        }
        try {
            lockInternals.releaseLock(lockData.lockPath);
        } finally {
            threadData.remove(thread);
        }
        return true;
    }

    public boolean internalLock() throws Exception {
        //重复上锁 不进行zk的具体操作，使用原子自增完成
        Thread thread = Thread.currentThread();
        LockData lockData = threadData.get(thread);
        if (Objects.nonNull(lockData)) {
            lockData.atomicInteger.incrementAndGet();
            return true;
        }
        //加锁 创建锁节点
        String lockPath = lockInternals.attemptLock();
        if (null != lockPath) {
            //put
            threadData.put(thread, new LockData(thread, lockPath));
            System.out.println("上锁成功！锁节点：" + lockPath);
            return true;
        }
        return false;
    }

}
